#include "HTTP.h"

extern GlobalCounter gCounter;

HTTP::HTTP()
{}

int HTTP::parse(Packet* pkt)
{
	int totalLength;
	if(0 >= (totalLength = pkt->pktLength - pkt->parsePos))
	{
		pkt->state = STATE_FAIL;
		return -1;
	}

	uint8_t* data = pkt->rawPkt + pkt->parsePos;

	int parseLength = 0;

	int fieldLength = 0;
	int rv =0;
	if(0 == (rv = inCheck(data, &fieldLength)))
	{
		if(300 <= pkt->pktLength - pkt->parsePos)
		{
			memset(headerBuffer, 0, 301);
			memcpy(headerBuffer, data, 300);
			string payload((char*)headerBuffer);
			transform(payload.begin(), payload.end(), payload.begin(), (int (*)(int))tolower);
			string headStart = "<head>";
			if(payload.find(headStart) < payload.length())
			{
				++gCounter.tcpHeadCount;
			}
		}
		pkt->state = STATE_FAIL;
		return -1;
	}
	//request
	else if(0 < rv)
	{
		parseLength += fieldLength;

		if(' ' != *(data + parseLength))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		++parseLength;

		//找不到请求行结尾的"\r\n"
		if(2 > (fieldLength = getStrLenBy0X0D0A(data + parseLength, totalLength - parseLength)))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		//URI = data + parseLength;
		//URILen = fieldLen - 10;
		parseLength += fieldLength;

		//非法的协议版本
		if(0 <= (rv = inCheck(data + parseLength - 10, &fieldLength)))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}

		//处理头部
		int headerCount = 0;
		while(2 < (fieldLength = getStrLenBy0X0D0A(data + parseLength, totalLength - parseLength)))
		{
			memset(headerBuffer, 0, HEADER_BUFFER_LENGTH);
			memcpy(headerBuffer, data + parseLength, fieldLength);

			char* pColon =strchr((char*)headerBuffer, ':');
			if(pColon)
			{
				++headerCount;
				headerBuffer[fieldLength - 2] = 0;
			//	printf("%s\n", headerBuffer);

				//name
				char* p = pColon - 1;
				for(; p != (char*)headerBuffer && *p == ' '; p--);
				*(p + 1) = 0;
				string name((char*)headerBuffer);
				transform(name.begin(), name.end(), name.begin(), (int (*)(int))tolower);
				//value
				p = pColon + 1;
				for(; *p!=0 && *p==' '; p++);
				string value(p);
				transform(value.begin(), value.end(), value.begin(), (int (*)(int))tolower);
			}

			parseLength += fieldLength;
		}
		if(0 < fieldLength)
		{
			parseLength += fieldLength;
		}
		//未提取到"\r\n"，可能是非标准协议
		if(0 == headerCount)
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
	}
	//response
	else
	{
		parseLength += fieldLength;

		if(' ' != *(data + parseLength))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		++parseLength;

		//状态码后空格
		if(' ' != *(data + parseLength + 3))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		memset(headerBuffer, 0, 3 + 1);
		memcpy(headerBuffer, data + parseLength, 3);
		if(200 == atoi((char*)headerBuffer))
		{
			pkt->httpResSuc = true;
		}
		else
		{
			pkt->httpResSuc = false;
		}
		parseLength += 4;

		//找不到应答行结尾的"\r\n"
		if(2 > (fieldLength = getStrLenBy0X0D0A(data + parseLength, totalLength - parseLength)))
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		parseLength += fieldLength;

		//处理头部
		int headerCount = 0;
		while(2 < (fieldLength = getStrLenBy0X0D0A(data + parseLength, totalLength - parseLength)))
		{
			memset(headerBuffer, 0, HEADER_BUFFER_LENGTH);
			memcpy(headerBuffer, data + parseLength, fieldLength);

			char* pColon =strchr((char*)headerBuffer, ':');
			if(pColon)
			{
				++headerCount;
				headerBuffer[fieldLength - 2] = 0;
			//	printf("%s\n", headerBuffer);

				//name
				char* p = pColon - 1;
				for(; p != (char*)headerBuffer && *p == ' '; p--);
				*(p + 1) = 0;
				string name((char*)headerBuffer);
				transform(name.begin(), name.end(), name.begin(), (int (*)(int))tolower);
				//value
				p = pColon + 1;
				for(; *p!=0 && *p==' '; p++);
				string value(p);
				transform(value.begin(), value.end(), value.begin(), (int (*)(int))tolower);

				//check content-type
				if("content-type" == name)
				{
					string html = "text/html";
					if(value.find(html) < value.length())
					{
						pkt->state = STATE_HTML;
					}
				}
			}

			parseLength += fieldLength;
		}
		if(0 < fieldLength)
		{
			parseLength += fieldLength;
		}
		//未提取到"\r\n"，可能是非标准协议
		if(0 == headerCount)
		{
			pkt->state = STATE_FAIL;
			return -1;
		}
		else
		{
			++gCounter.httpResCount;
		}
	}

	pkt->parsePos += parseLength;

	if(STATE_HTTP == pkt->state)
		pkt->state = STATE_FIN;

	return 0;
}

int HTTP::getStrLenBy0X0D0A(const uint8_t* src, int maxlen)
{
	int i;
	for(i=0; i<maxlen-1; i++)
	{
		if(0x0d == src[i] && 0x0a == src[i+1])
		{
			return i+2;
		}
	}

	return -1;
}

/*
 * check whether pkt is a legal http pkt, returns:
 * -4 http 0.9
 * -3 http 1.0
 * -2 http 2.0
 * -1 http 1.1
 * 0 not a http pkt
 * 1 get
 * 2 post
 * 3 head
 * 4 put
 * 5 delete
 * 6 trace
 * 7 connect
 * 8 options
 */
//改成直接全小写判断是否相等
int HTTP::inCheck(uint8_t* data, int* fieldLength)
{
	if (1 == strCmp((char*)data, "GET"))
	{
		*fieldLength = 3;
		return 1;
	}
	else if (1 == strCmp((char*)data, "POST"))
	{
		*fieldLength = 4;
		return 2;
	}
	else if (1 == strCmp((char*)data, "HEAD"))
	{
		*fieldLength = 4;
		return 3;
	}
	else if (1 == strCmp((char*)data, "PUT"))
	{
		*fieldLength = 3;
		return 4;
	}
	else if (1 == strCmp((char*)data, "DELETE"))
	{
		*fieldLength = 6;
		return 5;
	}
	else if (1 == strCmp((char*)data, "TRACE"))
	{
		*fieldLength = 5;
		return 6;
	}
	else if (1 == strCmp((char*)data, "CONNECT"))
	{
		*fieldLength = 7;
		return 7;
	}
	else if (1 == strCmp((char*)data, "OPTIONS"))
	{
		*fieldLength = 7;
		return 8;
	}
	else if (1 == strCmp((char*)data, "HTTP/1.1"))
	{
		*fieldLength = 8;
		return -1;
	}
	else if (1 == strCmp((char*)data, "HTTP/2.0"))
	{
		*fieldLength = 8;
		return -2;
	}
	else if (1 == strCmp((char*)data, "HTTP/1.0"))
	{
		*fieldLength = 8;
		return -3;
	}
	else if (1 == strCmp((char*)data, "HTTP/0.9"))
	{
		*fieldLength = 8;
		return -4;
	}
	else
	{
		return 0;
	}

	return 0;
}

/*
 * return -1 when src is shorter than dst or not match
 * return 1 when src IS started by dst
 */
int HTTP::strCmp(char* src, char* dst)
{
	int srcChar, dstChar;
	int i;
	for (i = 0; dst[i] != 0; i++)
	{
		//src is shorter than dst
		if(0 == src[i])
		{
			return -1;
		}
		srcChar = tolower(src[i]);
		dstChar = tolower(dst[i]);
		//not match
		if(srcChar != dstChar)
		{
			return -1;
		}
	}

	return 1;
}
